/***************************************************************************
 *
 * Copyright 2015-2019 BES.
 * All rights reserved. All unpublished rights reserved.
 *
 * No part of this work may be used or reproduced in any form or by any
 * means, or stored in a database or retrieval system, without prior written
 * permission of BES.
 *
 * Use of this work is governed by a license granted by BES.
 * This work contains confidential and proprietary information of
 * BES. which is protected by copyright, trade secret,
 * trademark and other intellectual property rights.
 *
 ****************************************************************************/
#ifndef __HAL_TRACE_H__
#define __HAL_TRACE_H__

#ifdef __cplusplus
extern "C" {
#endif
#include "string.h"
#include "plat_types.h"
#include "hal_trace_mod.h"

#define _CLOSE_SHUTDOWN_  (1)

#if 0
#define AUDIO_DEBUG
#define AUDIO_DEBUG_V0_1_0
#define INTERSYS_RAW_DATA_ONLY
#ifdef AUDIO_DEBUG
#undef TRACE_BAUD_RATE
#define TRACE_BAUD_RATE             2000000
#endif
#endif

#if (defined(AUDIO_DEBUG) && !defined(AUDIO_DEBUG_V0_1_0))
#define NO_REL_TRACE
#endif
#if (defined(AUDIO_DEBUG) && !defined(AUDIO_DEBUG_V0_1_0)) || \
        defined(INTERSYS_RAW_DATA_ONLY)
#define NO_TRACE
#endif

#if defined(__BT_DEBUG_TPORTS__) || defined(AUDIO_DEBUG_V0_1_0)
#ifndef TPORTS_KEY_COEXIST
#define HAL_TRACE_RX_ENABLE
#ifndef CRASH_REBOOT
#define CRASH_REBOOT
#endif
#endif
#endif

/*
 * Total number of core registers stored
 */
#define CRASH_DUMP_REGISTERS_NUM            17
#define CRASH_DUMP_REGISTERS_NUM_BYTES      ((CRASH_DUMP_REGISTERS_NUM)*4)

/*
 * Number bytes to store from stack
 *   - this is total, not per PSP/MSP
 */
#define CRASH_DUMP_STACK_NUM_BYTES          384

// Log Attributes
#define LOG_ATTR_ARG_NUM_SHIFT              0
#define LOG_ATTR_ARG_NUM_MASK               (0xF << LOG_ATTR_ARG_NUM_SHIFT)
#define LOG_ATTR_ARG_NUM(n)                 BITFIELD_VAL(LOG_ATTR_ARG_NUM, n)
#define LOG_ATTR_LEVEL_SHIFT                4
#define LOG_ATTR_LEVEL_MASK                 (0x7 << LOG_ATTR_LEVEL_SHIFT)
#define LOG_ATTR_LEVEL(n)                   BITFIELD_VAL(LOG_ATTR_LEVEL, n)
#define LOG_ATTR_MOD_SHIFT                  7
#define LOG_ATTR_MOD_MASK                   (0x7F << LOG_ATTR_MOD_SHIFT)
#define LOG_ATTR_MOD(n)                     BITFIELD_VAL(LOG_ATTR_MOD, n)
#define LOG_ATTR_IMM                        (1 << 14)
#define LOG_ATTR_NO_LF                      (1 << 15)
#define LOG_ATTR_NO_TS                      (1 << 16)
#define LOG_ATTR_NO_ID                      (1 << 17)

// Count variadic argument number
#define _VAR_ARG_12(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, ...) a12
#define COUNT_ARG_NUM(...)                  _VAR_ARG_12(unused, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

#if defined(TRACE_STR_SECTION) && !(defined(ROM_BUILD) || defined(PROGRAMMER) || defined(FPGA))
#define CONCAT_(x,y)                        x##y
#define CONCATS(x,y)                        CONCAT_(x,y)
#define __trcname                           CONCATS(__trc, __LINE__)
#define TRC_STR_LOC                         __attribute__((section(TO_STRING(CONCATS(.trc_str,__LINE__)))))
#define TRC_STR(s)                          (({ static const char TRC_STR_LOC __trcname[] = (s); __trcname; }))
#else
#define TRC_STR_LOC
#define TRC_STR(s)                          (s)
#endif

#define LOG_DUMMY(attr, str, ...)           hal_trace_dummy(str, ##__VA_ARGS__)

#if (defined(DEBUG) || defined(REL_TRACE_ENABLE)) && !defined(NO_REL_TRACE)
#define REL_LOG(attr, str, ...)             hal_trace_printf(((attr) & ~LOG_ATTR_ARG_NUM_MASK) | \
                                                            LOG_ATTR_ARG_NUM(COUNT_ARG_NUM(unused, ##__VA_ARGS__)), \
                                                            TRC_STR(str), ##__VA_ARGS__)
#define REL_LOG_RAW_OUTPUT(str, len)        hal_trace_output(str, len)
#define REL_LOG_FLUSH()                     hal_trace_flush_buffer()
#define REL_DUMP8(str, buf, cnt)            hal_trace_dump(str, sizeof(uint8_t), cnt, buf)
#define REL_DUMP16(str, buf, cnt)           hal_trace_dump(str, sizeof(uint16_t), cnt, buf)
#define REL_DUMP32(str, buf, cnt)           hal_trace_dump(str, sizeof(uint32_t), cnt, buf)
#else
#define REL_LOG(attr, str, ...)             hal_trace_dummy(str, ##__VA_ARGS__)
#define REL_LOG_RAW_OUTPUT(str, len)        hal_trace_dummy((const char *)str, len)
#define REL_LOG_FLUSH()                     hal_trace_dummy(NULL)
#define REL_DUMP8(str, buf, cnt)            hal_dump_dummy(str, buf, cnt)
#define REL_DUMP16(str, buf, cnt)           hal_dump_dummy(str, buf, cnt)
#define REL_DUMP32(str, buf, cnt)           hal_dump_dummy(str, buf, cnt)
#endif

#if (!defined(DEBUG) && defined(REL_TRACE_ENABLE)) && !defined(NO_TRACE)
// To avoid warnings on unused variables
#define NORM_LOG(num,str, ...)              hal_trace_dummy(str, ##__VA_ARGS__)
#define NORM_LOG_RAW_OUTPUT(str, len)       hal_trace_dummy((const char *)str, len)
#define NORM_LOG_FLUSH()                    hal_trace_dummy(NULL)
#define DUMP8(str, buf, cnt)                hal_dump_dummy(str, buf, cnt)
#define DUMP16(str, buf, cnt)               hal_dump_dummy(str, buf, cnt)
#define DUMP32(str, buf, cnt)               hal_dump_dummy(str, buf, cnt)
#else
#define NORM_LOG                            REL_LOG
#define NORM_LOG_RAW_OUTPUT                 REL_LOG_RAW_OUTPUT
#define NORM_LOG_FLUSH                      REL_TRACE_FLUSH
#define DUMP8                               REL_DUMP8
#define DUMP16                              REL_DUMP16
#define DUMP32                              REL_DUMP32
#endif

#define RLOG_CRITICAL(attr, str, ...)       REL_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_CRITICAL), \
                                                    str, ##__VA_ARGS__)
#define RLOG_ERROR(attr, str, ...)          REL_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_ERROR), \
                                                    str, ##__VA_ARGS__)
#define RLOG_WARN(attr, str, ...)           REL_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_WARN), \
                                                    str, ##__VA_ARGS__)
#define RLOG_NOTIF(attr, str, ...)          REL_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_NOTIF), \
                                                    str, ##__VA_ARGS__)
#define RLOG_INFO(attr, str, ...)           REL_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_INFO), \
                                                    str, ##__VA_ARGS__)
#define RLOG_DEBUG(attr, str, ...)          REL_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_DEBUG), \
                                                    str, ##__VA_ARGS__)
#define RLOG_VERBOSE(attr, str, ...)        REL_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_VERBOSE), \
                                                    str, ##__VA_ARGS__)

#define LOG_CRITICAL(attr, str, ...)        NORM_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_CRITICAL), \
                                                    str, ##__VA_ARGS__)
#define LOG_ERROR(attr, str, ...)           NORM_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_ERROR), \
                                                    str, ##__VA_ARGS__)
#define LOG_WARN(attr, str, ...)            NORM_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_WARN), \
                                                    str, ##__VA_ARGS__)
#define LOG_NOTIF(attr, str, ...)           NORM_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_NOTIF), \
                                                    str, ##__VA_ARGS__)
#define LOG_INFO(attr, str, ...)            NORM_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_INFO), \
                                                    str, ##__VA_ARGS__)
#define LOG_DEBUG(attr, str, ...)           NORM_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_DEBUG), \
                                                    str, ##__VA_ARGS__)
#define LOG_VERBOSE(attr, str, ...)         NORM_LOG(((attr) & ~LOG_ATTR_LEVEL_MASK) | LOG_ATTR_LEVEL(LOG_LEVEL_VERBOSE), \
                                                    str, ##__VA_ARGS__)

#define REL_TRACE(attr, str, ...)           RLOG_NOTIF(attr, str, ##__VA_ARGS__)
#define REL_TRACE_IMM(attr, str, ...)       RLOG_NOTIF((attr) | LOG_ATTR_IMM, str, ##__VA_ARGS__)
#define REL_TRACE_NOCRLF(attr, str, ...)    RLOG_NOTIF((attr) | LOG_ATTR_NO_LF, str, ##__VA_ARGS__)
#define REL_TRACE_NOTS(attr, str, ...)      RLOG_NOTIF((attr) | LOG_ATTR_NO_TS, str, ##__VA_ARGS__)
#define REL_TRACE_IMM_NOTS(attr, str, ...)  RLOG_NOTIF((attr) | LOG_ATTR_IMM | LOG_ATTR_NO_TS, str, ##__VA_ARGS__)
#define REL_TRACE_NOCRLF_NOTS(attr, str, ...) RLOG_NOTIF((attr) | LOG_ATTR_NO_LF | LOG_ATTR_NO_TS, str, ##__VA_ARGS__)
#define REL_FUNC_ENTRY_TRACE()              RLOG_NOTIF(1, "%s", __FUNCTION__)
#define REL_TRACE_OUTPUT(str, len)          REL_LOG_RAW_OUTPUT(str, len)
#define REL_TRACE_FLUSH()                   REL_LOG_FLUSH()


#if defined(AUDIO_DEBUG_V0_1_0)
#define TRACE(attr, str, ...)               LOG_INFO(attr, str,  ##__VA_ARGS__)
#else
#define CATFILENAME(x) strrchr(x,'/')?strrchr(x,'/')+1:x
#define TRACE(attr, str, ...)               LOG_INFO(attr, "[%s:%d]"str, CATFILENAME(__FILE__), __LINE__,   ##__VA_ARGS__)
#endif
#define __PSENSOR_PRINT__
#ifndef __PSENSOR_PRINT__
#define TRACE_PSENSOR(attr, str, ...) 
#else
#define TRACE_PSENSOR(attr, str, ...)               LOG_INFO(attr, str, ##__VA_ARGS__)
#endif
#define TRACE_KEY(attr, str, ...)          LOG_INFO(attr, str, ##__VA_ARGS__)
#define TRACE_IMM(attr, str, ...)           LOG_INFO((attr) | LOG_ATTR_IMM, str, ##__VA_ARGS__)
#define TRACE_NOCRLF(attr, str, ...)        LOG_INFO((attr) | LOG_ATTR_NO_LF, str, ##__VA_ARGS__)
#define FUNC_ENTRY_TRACE()                  LOG_INFO(1, "%s", __FUNCTION__)
#define TRACE_OUTPUT(str, len)              NORM_LOG_RAW_OUTPUT(str, len)
#define TRACE_FLUSH()                       NORM_LOG_FLUSH()
#ifdef BES_AUTOMATE_TEST
#define AUTO_TEST_TRACE(attr, str, ...)     LOG_INFO(attr, "_AT_"str, ##__VA_ARGS__)
#else
#define AUTO_TEST_TRACE(attr, str, ...)     LOG_INFO(attr, str, ##__VA_ARGS__)
#endif

#define TRACE_DUMMY(attr, str, ...)         LOG_DUMMY(attr, str, ##__VA_ARGS__)

#if (defined(DEBUG) || defined(REL_TRACE_ENABLE)) && defined(ASSERT_SHOW_FILE_FUNC)
#define ASSERT(cond, str, ...)      { if (!(cond)) { hal_trace_assert_dump(__FILE__, __FUNCTION__, __LINE__, str, ##__VA_ARGS__); } }
#define ASSERT_DUMP_ARGS            const char *file, const char *func, unsigned int line, const char *fmt, ...
#define ASSERT_FMT_ARG_IDX          4
#elif (defined(DEBUG) || defined(REL_TRACE_ENABLE)) && defined(ASSERT_SHOW_FILE)
#define ASSERT(cond, str, ...)      { if (!(cond)) { hal_trace_assert_dump(__FILE__, __LINE__, str, ##__VA_ARGS__); } }
#define ASSERT_DUMP_ARGS            const char *file, const char *func, unsigned int line, const char *fmt, ...
#define ASSERT_FMT_ARG_IDX          4
#elif (defined(DEBUG) || defined(REL_TRACE_ENABLE)) && defined(ASSERT_SHOW_FUNC)
#define ASSERT(cond, str, ...)      { if (!(cond)) { hal_trace_assert_dump(__FUNCTION__, __LINE__, str, ##__VA_ARGS__); } }
#define ASSERT_DUMP_ARGS            const char *scope, unsigned int line, const char *fmt, ...
#define ASSERT_FMT_ARG_IDX          3
#elif (defined(DEBUG) || defined(REL_TRACE_ENABLE))
#define ASSERT(cond, str, ...)      { if (!(cond)) { hal_trace_assert_dump(str, ##__VA_ARGS__); } }
#define ASSERT_DUMP_ARGS            const char *fmt, ...
#define ASSERT_FMT_ARG_IDX          1
#else
#define ASSERT(cond, str, ...)      { if (!(cond)) { hal_trace_dummy(str, ##__VA_ARGS__); hal_trace_assert_dump(NULL); } }
#define ASSERT_DUMP_ARGS            const char *fmt
#define ASSERT_FMT_ARG_IDX          0
#endif

#if (defined(DEBUG) || defined(REL_TRACE_ENABLE))
#define TRACE_FUNC_DECLARE(d, r)    d
#else
#ifndef TRACE_FUNC_SPEC
#define TRACE_FUNC_SPEC             static inline
#endif
#define TRACE_FUNC_DECLARE(d, r)    TRACE_FUNC_SPEC d { r; }
#endif

#if defined(__GNUC__) && !defined(NO_CHK_TRC_FMT)
#define TRC_FMT_CHK(sidx, vaidx)    __attribute__((format(printf, (sidx), (vaidx))))
#else
#define TRC_FMT_CHK(sidx, vaidx)
#endif

#define ASSERT_NODUMP(cond)         { if (!(cond)) { SAFE_PROGRAM_STOP(); } }

#ifdef CHIP_BEST1000
// Avoid CPU instruction fetch blocking the system bus on BEST1000
#define SAFE_PROGRAM_STOP()         do { asm volatile("nop; nop; nop; nop"); } while (1)
#else
#define SAFE_PROGRAM_STOP()         do { } while (1)
#endif

enum HAL_TRACE_TRANSPORT_T {
#ifdef CHIP_HAS_USB
    HAL_TRACE_TRANSPORT_USB,
#endif
    HAL_TRACE_TRANSPORT_UART0,
#if (CHIP_HAS_UART > 1)
    HAL_TRACE_TRANSPORT_UART1,
#endif
#if (CHIP_HAS_UART > 2)
    HAL_TRACE_TRANSPORT_UART2,
#endif

    HAL_TRACE_TRANSPORT_QTY
};

enum HAL_TRACE_STATE_T {
    HAL_TRACE_STATE_CRASH_ASSERT_START,
    HAL_TRACE_STATE_CRASH_FAULT_START,
    HAL_TRACE_STATE_CRASH_END,
};

enum HAL_TRACE_BUF_STATE_T {
    HAL_TRACE_BUF_STATE_FLUSH,
    HAL_TRACE_BUF_STATE_NEAR_FULL,
    HAL_TRACE_BUF_STATE_FULL,
};

enum HAL_TRACE_CRASH_DUMP_MODULE_T {
    HAL_TRACE_CRASH_DUMP_MODULE_SYS = 0,
    HAL_TRACE_CRASH_DUMP_MODULE_ID1 = 1,
    HAL_TRACE_CRASH_DUMP_MODULE_ID2 = 2,
    HAL_TRACE_CRASH_DUMP_MODULE_ID3 = 3,
    HAL_TRACE_CRASH_DUMP_MODULE_BT  = 4,
    HAL_TRACE_CRASH_DUMP_MODULE_END = 5,
};

enum LOG_LEVEL_T {
    LOG_LEVEL_CRITICAL  = 0,
    LOG_LEVEL_ERROR     = 1,
    LOG_LEVEL_WARN      = 2,
    LOG_LEVEL_NOTIF     = 3,
    LOG_LEVEL_INFO      = 4,
    LOG_LEVEL_DEBUG     = 5,
    LOG_LEVEL_VERBOSE   = 6,

    LOG_LEVEL_QTY,
};

typedef void (*HAL_TRACE_CRASH_DUMP_CB_T)(void);

typedef void (*HAL_TRACE_APP_NOTIFY_T)(enum HAL_TRACE_STATE_T state);

typedef void (*HAL_TRACE_APP_OUTPUT_T)(const unsigned char *buf, unsigned int buf_len);

typedef void (*HAL_TRACE_BUF_CTRL_T)(enum HAL_TRACE_BUF_STATE_T buf_ctrl);

int hal_trace_open(enum HAL_TRACE_TRANSPORT_T transport);

int hal_trace_open_cp(void);

TRACE_FUNC_DECLARE(int hal_trace_switch(enum HAL_TRACE_TRANSPORT_T transport), return 0);

TRACE_FUNC_DECLARE(int hal_trace_close(void), return 0);

TRACE_FUNC_DECLARE(int hal_trace_enable_log_module(enum LOG_MODULE_T module), return 0);

TRACE_FUNC_DECLARE(int hal_trace_disable_log_module(enum LOG_MODULE_T module), return 0);

TRACE_FUNC_DECLARE(int hal_trace_set_log_module(const uint32_t *map, uint32_t word_cnt), return 0);

TRACE_FUNC_DECLARE(int hal_trace_set_log_level(enum LOG_LEVEL_T level), return 0);

TRACE_FUNC_DECLARE(void hal_trace_get_history_buffer(const unsigned char **buf1, unsigned int *len1, \
    const unsigned char **buf2, unsigned int *len2), \
    { if (buf1) { *buf1 = NULL; } if (len1) { *len1 = 0; } if (buf2) { *buf2 = NULL; } if (len2) { *len2 = 0; } });

TRACE_FUNC_DECLARE(int hal_trace_output(const unsigned char *buf, unsigned int buf_len), return 0);

TRC_FMT_CHK(2, 3)
TRACE_FUNC_DECLARE(int hal_trace_printf(uint32_t attr, const char *fmt, ...), return 0);

TRACE_FUNC_DECLARE(int hal_trace_dump(const char *fmt, unsigned int size,  unsigned int count, const void *buffer), return 0);

TRACE_FUNC_DECLARE(int hal_trace_busy(void), return 0);

TRACE_FUNC_DECLARE(int hal_trace_pause(void), return 0);

TRACE_FUNC_DECLARE(int hal_trace_continue(void), return 0);

TRACE_FUNC_DECLARE(int hal_trace_flush_buffer(void), return 0);

TRACE_FUNC_DECLARE(void hal_trace_idle_send(void), return);

TRACE_FUNC_DECLARE(int hal_trace_crash_dump_register(enum HAL_TRACE_CRASH_DUMP_MODULE_T module, HAL_TRACE_CRASH_DUMP_CB_T cb), return 0);

TRACE_FUNC_DECLARE(void hal_trace_app_register(HAL_TRACE_APP_NOTIFY_T notify_cb, HAL_TRACE_APP_OUTPUT_T output_cb), return);

TRACE_FUNC_DECLARE(void hal_trace_app_custom_register(HAL_TRACE_APP_NOTIFY_T notify_cb, HAL_TRACE_APP_OUTPUT_T output_cb, HAL_TRACE_APP_OUTPUT_T crash_custom_cb), return);

TRACE_FUNC_DECLARE(void hal_trace_cp_register(HAL_TRACE_APP_NOTIFY_T notify_cb, HAL_TRACE_BUF_CTRL_T buf_cb), return);

TRACE_FUNC_DECLARE(void hal_trace_print_backtrace(uint32_t addr, uint32_t search_cnt, uint32_t print_cnt), return);

TRACE_FUNC_DECLARE(bool hal_trace_crash_dump_onprocess(void), return false);

TRACE_FUNC_DECLARE(uint32_t hal_trace_get_baudrate(void), return 0);

TRC_FMT_CHK(1, 2)
static inline void hal_trace_dummy(const char *fmt, ...) { }

static inline void hal_dump_dummy(const char *fmt, ...) { }

#if (ASSERT_FMT_ARG_IDX > 0)
TRC_FMT_CHK(ASSERT_FMT_ARG_IDX, ASSERT_FMT_ARG_IDX + 1)
#endif
void NORETURN hal_trace_assert_dump(ASSERT_DUMP_ARGS);

int hal_trace_address_writable(uint32_t addr);

int hal_trace_address_executable(uint32_t addr);

int hal_trace_address_readable(uint32_t addr);


//==============================================================================
// AUDIO_DEBUG
//==============================================================================

#ifdef AUDIO_DEBUG
#define AUDIO_DEBUG_TRACE(str, ...)         hal_trace_printf(str, ##__VA_ARGS__)
#define AUDIO_DEBUG_DUMP(buf, cnt)          hal_trace_output(buf, cnt)
#endif


//==============================================================================
// INTERSYS_RAW_DATA_ONLY
//==============================================================================

#ifdef INTERSYS_RAW_DATA_ONLY
#define TRACE_RAW(str, ...)                 hal_trace_printf(str, ##__VA_ARGS__)
#define DUMP8_RAW(str, buf, cnt)            hal_trace_dump(str, sizeof(uint8_t), cnt, buf)
#endif


//==============================================================================
// TRACE RX
//==============================================================================

#if defined(_AUTO_TEST_)
#define HAL_TRACE_RX_ENABLE
extern int auto_test_send(char *resp);
#define AUTO_TEST_SEND(str)         auto_test_send((char*)str)
#endif

#include "stdio.h"

#define hal_trace_rx_parser(buf, str, ...)  sscanf(buf, str, ##__VA_ARGS__)

typedef unsigned int (*HAL_TRACE_RX_CALLBACK_T)(unsigned char *buf, unsigned int  len);

int hal_trace_rx_register(const char *name, HAL_TRACE_RX_CALLBACK_T callback);
int hal_trace_rx_deregister(const char *name);
int hal_trace_rx_reopen();
int hal_trace_rx_open();


#ifdef __cplusplus
}
#endif

#endif
